package com.gxar.marketdelivery.domain.validator;

import com.gxar.marketdelivery.domain.aviator.enums.PeriodType;
import com.gxar.marketdelivery.domain.aviator.enums.WeekDay;
import com.gxar.marketdelivery.interfaces.web.result.IResponseStatusMsg;
import com.gxar.marketdelivery.shared.model.DeliveryRuleModel;
import com.gxar.marketdelivery.shared.model.PeriodModel;
import com.gxar.marketdelivery.shared.utils.AssertUtils;
import com.gxar.marketdelivery.shared.utils.CollectionUtils;

import java.util.List;

/**
 * @author nice
 * @version 1.0
 * @created 2023/7/23.
 */
public class DeliveryRuleValidator {

    public static final int MINUTES_OF_ONE_DAY = 1440;

    public static void validateRule(DeliveryRuleModel ruleModel) {
        AssertUtils.notNull(ruleModel, IResponseStatusMsg.APIEnum.PARAM_ERROR, "投放规则为空");
        Long startTime = ruleModel.getStartTime();
        Long endTime = ruleModel.getEndTime();
        if (startTime != null && endTime != null) {
            AssertUtils.isTrue(startTime <= endTime, IResponseStatusMsg.APIEnum.PARAM_ERROR,
                    "开始时间必须小于结束时间");
        } else {
            AssertUtils.isTrue(startTime == null && endTime == null, IResponseStatusMsg.APIEnum.PARAM_ERROR,
                    "开始时间和结束时间必须同时为空或同时不为空");
        }
        validatePeriod(ruleModel.getPeriodModels(), ruleModel.getPeriodType());
    }

    public static void validatePeriod(List<PeriodModel> periodModels, Integer periodType) {
        if (CollectionUtils.isEmpty(periodModels)) {
            return;
        }
        if (periodType == null) {
            return;
        }
        AssertUtils.isTrue(PeriodType.contains(periodType), IResponseStatusMsg.APIEnum.PARAM_ERROR, "时间类型不合法");
        PeriodType type = PeriodType.getByCode(periodType);

        switch (type) {
        case SPECIFIC_HOUR:
            validatePeriodHour(periodModels);
            break;
        case SPECIFIC_WEEK:
            validatePeriodWeek(periodModels);
            break;
        case MULTI_SPECIFIC_HOURS:
            validatePeriodMultiHour(periodModels);
            break;
        default:
            break;
        }
    }

    private static void validatePeriodHour(List<PeriodModel> periodModels) {
        AssertUtils.equal(periodModels.size(), 1, IResponseStatusMsg.APIEnum.PARAM_ERROR, "每天指定时间段只能指定一个");
        periodModels.forEach(info -> {
            AssertUtils.isTrue(info.getFrom() < info.getTo(), IResponseStatusMsg.APIEnum.PARAM_ERROR,
                    "起始时间必须小于结束时间");
            AssertUtils.isTrue(info.getFrom() >= 0 && info.getFrom() < MINUTES_OF_ONE_DAY,
                    IResponseStatusMsg.APIEnum.PARAM_ERROR, "起始时间取值不合法");
            AssertUtils.isTrue(info.getTo() >= 0 && info.getTo() < MINUTES_OF_ONE_DAY,
                    IResponseStatusMsg.APIEnum.PARAM_ERROR, "结束时间取值不合法");
        });
    }

    private static void validatePeriodWeek(List<PeriodModel> periodModels) {
        AssertUtils.equal(periodModels.size(), 1, IResponseStatusMsg.APIEnum.PARAM_ERROR,
                "每周指定星期指定时间只能指定一个");
        periodModels.forEach(info -> {
            AssertUtils.notNull(info.getDayOfWeek(), IResponseStatusMsg.APIEnum.PARAM_ERROR, "每周的天不能为空");
            AssertUtils.isTrue(WeekDay.contains(info.getDayOfWeek()), IResponseStatusMsg.APIEnum.PARAM_ERROR,
                    "不支持的WEEK类型");
            AssertUtils.isTrue(info.getFrom() < info.getTo(), IResponseStatusMsg.APIEnum.PARAM_ERROR,
                    "起始时间必须小于结束时间");
            AssertUtils.isTrue(info.getFrom() >= 0 && info.getFrom() < MINUTES_OF_ONE_DAY,
                    IResponseStatusMsg.APIEnum.PARAM_ERROR, "起始时间取值不合法");
            AssertUtils.isTrue(info.getTo() >= 0 && info.getTo() < MINUTES_OF_ONE_DAY,
                    IResponseStatusMsg.APIEnum.PARAM_ERROR, "结束时间取值不合法");
        });
    }

    private static void validatePeriodMultiHour(List<PeriodModel> periodModels) {
        periodModels.forEach(info -> {
            AssertUtils.isTrue(info.getFrom() < info.getTo(), IResponseStatusMsg.APIEnum.PARAM_ERROR,
                    "起始时间必须小于结束时间");
            AssertUtils.isTrue(info.getFrom() >= 0 && info.getFrom() < MINUTES_OF_ONE_DAY,
                    IResponseStatusMsg.APIEnum.PARAM_ERROR, "起始时间取值不合法");
            AssertUtils.isTrue(info.getTo() >= 0 && info.getTo() < MINUTES_OF_ONE_DAY,
                    IResponseStatusMsg.APIEnum.PARAM_ERROR, "结束时间取值不合法");
        });
        //判断时间段是否有交叉
        for (int i = 0; i < periodModels.size() - 1; i++) {
            for (int j = i + 1; j < periodModels.size(); j++) {
                AssertUtils.isTrue(!isOverlap(periodModels.get(i), periodModels.get(j)),
                        IResponseStatusMsg.APIEnum.PARAM_ERROR, "时间段有重叠，输入有误");
            }
        }
    }

    private static boolean isOverlap(PeriodModel range1, PeriodModel range2) {
        return Math.max(range1.getFrom(), range2.getFrom()) <= Math.min(range1.getTo(), range2.getTo());
    }
}
